﻿using ETModel;
using ETHotfix;
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using ET.HelloWorld.Hotfix.Handlers;
using ET.HelloWorld.Hotfix.Components;

namespace ET.HelloWorld.Hotfix.Entitys {


    [ObjectSystem]
    public class GameUnitAwakeSystem : AwakeSystem<GameUnit, long> {
        public override void Awake(GameUnit self, long playerId) {
            self.Awake(playerId);
        }
    }
    [ObjectSystem]
    public class GameUnitBulletCollisionObjectStartSystem : StartSystem<BulletCollisionObject> {
        public override void Start(BulletCollisionObject self) {
            var unit = self.GetParent<GameUnit>();
            if (unit == null) {
                Log.Error("物理对象组件只能附加给单位");
                return;
            }
            //物理对象组件附加成功后,启用单位的物理数据对接
            unit.UseBullet();
        }
    }

    public class GameUnit : Unit {
        protected BulletCollisionObject _bCollisionObj = null;
        protected UnitInfoSyncComponent _UnitInfoSync = null;

        protected Vector3 _Scale;
        protected Vector3 _Position;
        protected Quaternion _Rotation;
        /// <summary>
        /// 中心相对Position的偏移量
        /// </summary>
        protected Vector3 _Center;
        /// <summary>
        /// 中心在世界的坐标
        /// </summary>
        protected Vector3 _CenterPosition;

        /// <summary>
        /// 所使用的单位配置ID
        /// </summary>
        public long UnitConfigId;

        /// <summary>
        /// 攻击距离
        /// </summary>
        public float AttackDistance;

        /// <summary>
        /// 中心相对Position的偏移量,需要在位置前设置
        /// </summary>
        public Vector3 Center {
            get {
                return _Center;
            }
            set {
                _Center = value;
                _CenterPosition = _Position + _Center;
            }
        }

        public float MoveSpeed;
        public bool StateDie;
        public bool StateAttack;
        /// <summary>
        /// 攻击预备时间(即多少毫秒后真正开始计算攻击碰撞)
        /// </summary>
        public float AttackPrepareTime;
        /// <summary>
        /// 攻击总持续时间,有的动作不能在攻击过程中使用,比如下次攻击要在本次攻击结束后才可以继续
        /// </summary>
        public float AttackUseTime;

        public new void Awake(long playerId) {
            PlayerId = playerId;
            _Position = _Center = _CenterPosition = Vector3.zero;
            _Rotation = Quaternion.identity;
            _Scale = Vector3.one;
        }
        public override void Dispose() {
            if (this.IsDisposed) {
                return;
            }
            _bCollisionObj = null;
            _UnitInfoSync = null;

            base.Dispose();
        }


        /// <summary>
        /// 获取单位加载的物理对象组件
        /// </summary>
        /// <returns></returns>
        public BulletCollisionObject GetBulletCollisionObject() {
            if (_bCollisionObj == null) {
                _bCollisionObj = GetComponent<BulletCollisionObject>();
            }
            if (_bCollisionObj == null || _bCollisionObj.IsDisposed) return null;
            return _bCollisionObj;
        }
        /// <summary>
        /// 获取当前场景加载的单位信息同步客户端组件
        /// </summary>
        /// <returns></returns>
        public UnitInfoSyncComponent GetUnitInfoSync() {
            if (_UnitInfoSync == null) {
                _UnitInfoSync = Game.Scene.GetComponent<UnitInfoSyncComponent>();
            }
            if (_UnitInfoSync == null || _UnitInfoSync.IsDisposed) return null;
            return _UnitInfoSync;
        }

        #region 位置,旋转,缩放 操作

        /// <summary>
        /// 将位置,旋转合并为矩阵(用在物理引擎中)
        /// </summary>
        /// <returns></returns>
        public Matrix4x4 GetButtleWorldTransform() {
            return Matrix4x4.TRS(_CenterPosition, _Rotation, Vector3.one);
        }



        /// <summary>
        /// 设置单位中心在世界的坐标,如果启用了物理引擎,则会同步修改在物理世界中的值
        /// </summary>
        /// <param name="position"></param>
        public void SetCenterPosition(Vector3 centerPos) {
            SetPosition(centerPos - Center);
        }
        /// <summary>
        /// 单位中心点在世界的坐标
        /// </summary>
        /// <returns></returns>
        public Vector3 GetCenterPosition() {
            return _CenterPosition;
        }

        /// <summary>
        /// 设置单位地图位置(一般指单位脚底中心在世界的坐标),如果启用了物理引擎,则会同步修改在物理世界中的值
        /// </summary>
        /// <param name="position"></param>
        public override void SetPosition(Vector3 position) {
            _Position = position;
            _CenterPosition = _Position + _Center;
            //设置给物理对象
            UpdateBulletWorldTranform();
            //同步客户端
            SyncInfoToClient();
        }
        /// <summary>
        /// 获取单位地图位置
        /// </summary>
        /// <returns></returns>
        public override Vector3 GetPosition() {
            return _Position;
        }

        /// <summary>
        /// 设置当前旋转值,如果启用了物理引擎,则会同步修改在物理世界中的值
        /// </summary>
        /// <param name="position"></param>
        public void SetRotation(Quaternion rotation) {
            _Rotation = rotation;
            //设置给物理对象
            UpdateBulletWorldTranform();
            //同步客户端
            SyncInfoToClient();
        }
        /// <summary>
        /// 获取当前旋转值
        /// </summary>
        /// <returns></returns>
        public Quaternion GetRotation() {
            return _Rotation;
        }

        /// <summary>
        /// 设置当前缩放值,如果启用了物理引擎,则会同步修改在物理世界中的值
        /// </summary>
        /// <param name="position"></param>
        public void SetScale(Vector3 scale) {
            _Scale = scale;
            //设置给物理对象
            UpdateBulletWorldTranform();
            //同步客户端
            SyncInfoToClient();
        }
        /// <summary>
        /// 获取当前位置
        /// </summary>
        /// <returns></returns>
        public Vector3 GetScale() {
            return _Scale;
        }
        #endregion

        /// <summary>
        /// 将位置.旋转更新进物理引擎
        /// </summary>
        private void UpdateBulletWorldTranform() {
            GetBulletCollisionObject()?.SetWorldTransform(GetButtleWorldTransform());
        }
        /// <summary>
        /// 有更新世界矩阵就同步给客户端
        /// </summary>
        private void SyncInfoToClient() {
            GetUnitInfoSync()?.UpdateUnitMeshSync(Id, ref _CenterPosition, ref _Rotation, ref _Scale);
        }

        /// <summary>
        /// 启用物理引擎的数据对接,将物理数据同步给自己,并开始同步物理数据
        /// </summary>
        public void UseBullet() {
            var cObj = GetBulletCollisionObject();
            if (cObj == null || cObj.MotionState == null) {
                Log.Error("物理组件未正确加载,无法启用物理引擎");
                return;
            }

            //防止已经绑定过,先取消再绑定
            cObj.MotionState.OnWorldTransformChanged -= MotionState_OnWorldTransformChanged;
            cObj.MotionState.OnWorldTransformChanged += MotionState_OnWorldTransformChanged;

            //用物理对象的矩阵覆盖自己 (暂时不使用物理引擎里的缩放)
            _CenterPosition = cObj.GetWorldPosition();//物理世界中的坐标是中心点
            _Position = _CenterPosition - Center;//转为地图位置
            _Rotation = cObj.GetRotation();
            //同步客户端
            SyncInfoToClient();
        }

        private void MotionState_OnWorldTransformChanged(ref Vector3 newPos, ref Quaternion newRotation) {
            _CenterPosition = newPos;//物理世界中的坐标是中心点
            _Position = _CenterPosition - Center;//转为地图位置
            _Rotation = newRotation;
            //物理世界中修改了矩阵,同步给客户端
            SyncInfoToClient();
        }



        /// <summary>
        /// 构建出UnitInfo对象,用于传输
        /// </summary>
        /// <returns></returns>
        public UnitInfo BuildUnitInfo() {

            var unitInfo = new UnitInfo {
                PlayerId = PlayerId,
                UnitId = Id,
                UnitConfigId = UnitConfigId,
            };
            unitInfo.CenterX = _Center.x;
            unitInfo.CenterY = _Center.y;
            unitInfo.CenterZ = _Center.z;
            unitInfo.SetCenterPosition(ref _CenterPosition);
            unitInfo.SetRotation(ref _Rotation);
            unitInfo.SetScale(ref _Scale);

            //将单位的线框提取出来
            var shape = GetComponent<BulletCollisionShape>();
            if (shape != null) {
                //采用物理引擎的形状
                uint[] indices;
                var vertices = MeshFactory.CreateShapeForUnity3D(shape.Shape, out indices);
                unitInfo.ShapeIndices.Add(indices);
                foreach (var v in vertices) {
                    unitInfo.ShapeVerticesXs.Add(v.X);
                    unitInfo.ShapeVerticesYs.Add(v.Y);
                    unitInfo.ShapeVerticesZs.Add(v.Z);
                }
            }

            return unitInfo;
        }

        public override float GetMoveSpeed() {
            return MoveSpeed;
        }
    }
}
